package pacBioSimple;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.concurrent.Callable;

import tools.kmer.KmerSet_binary;
import tools.kmer.kmerFunctions;
import tools.sequences.sequenceUtils;

public class PacBioSeedBridgingSimple implements Callable<Path>{
	
	private StringBuilder seed1 = null;
	private StringBuilder seed2= null;
	private int distance=0;
	private List<StringBuilder> candidate = null;
	private List<StringBuilder> repeatsStoppPath = null;
	private PacBioNodeStack<Integer> stack=null;
	private CodingTable codingTable=null;
	private KmerSet_binary ks=null;
	private boolean dir=false; //false ende // true anfang
	private int id;
	private int repVal=0;
	private boolean stopp=false;
	private Path candidatePath = null;
	

	
	
	public PacBioSeedBridgingSimple(StringBuilder seed1, StringBuilder seed2, int distance,CodingTable table, KmerSet_binary ks,boolean dir,int repVal,Path candidatePath)
	{
		this.seed1 = seed1;
		this.seed2 = seed2;
		this.distance=distance+distance*10/100;
		this.codingTable=table;
		this.ks=ks;
		this.candidatePath = candidatePath;
		this.dir=dir;
		this.repVal=repVal;
		init();
	}
	
	public PacBioSeedBridgingSimple(StringBuilder seed1,int distance,CodingTable table, KmerSet_binary ks,boolean dir,int repVal,Path candidatePath)
	{
		this.seed1=seed1;
		this.distance=distance+distance*10/100;
		this.codingTable=table;
		this.ks=ks;
		this.candidatePath = candidatePath;
		this.dir=dir;
		this.repVal=repVal;
		init();
	}
	
	private void init()
	{
		stack = new PacBioNodeStack<Integer>();
		this.candidate=new ArrayList<StringBuilder>();
		this.repeatsStoppPath = new ArrayList<StringBuilder>();
		
	}
	
	private int checkKmerDistribution(String kmer)
	{
		//System.out.print(kmer+ " kmer check\n");
		if(kmer.equals(""))
		{
			return 0;
		}
		String kmerToUse = kmerFunctions.kmerToUse(kmer);
		BitSet bS = ks.getUtils().stringToBitSet(kmerToUse);
		//BitSet bS = ks.getUtils().stringToBitSet(kmerFunctions.kmerToUse(kmer.toString()));
		if(ks.exists(kmerToUse))
		{
			return(ks.getNumber(bS));
		}
		else
		{
			return 0;
		}
		
	}
	private void addCandidate(StringBuilder kmer)
	{
		if(dir)
		{
			this.candidate.add(sequenceUtils.reverseComplement2(kmer));
		}
		else
		{
			this.candidate.add(kmer);
		}
	}
	
	private StringBuilder pop(StringBuilder kmer, boolean mark)
	{
		if(!stack.empty() && mark)
		{
			addCandidate(new StringBuilder(kmer));
//			if(this.candidate.size()>=11)
//			{
//				this.stopp=true;
//			}
		}
		stack.pop();
		--id;
		kmer.deleteCharAt(kmer.length()-1);
		if(!stack.empty())
		{
			stack.setPeek(stack.peek()+1);  
		}
		return kmer;
	}
	
	public void run()
	{
		id=0;
		//here we have to fill all the nasty things building up the stack, saving the result
		//first add the first root node
		if(stack.empty())
		{
			stack.push(0);
			++id;
		}

		StringBuilder kmer=new StringBuilder(this.seed1);	
		int counter =0;
		while(!stack.empty() && !stopp)
		{		
			++counter;
//			log.log(Level.WARNING,"StackDeep: "+stack.getLength() + "StackNode: "+stack.peek().getId()+" "+stack.peek().getStatus() +" Distance "+ distance+"\n");
			//if(counter % 100==0)
			//System.out.println("StackDeep: "+stack.getLength() + " " +stack.peek() +" Distance "+ distance +"\n");
			//System.out.println("kmer "+kmer);
			if(stack.peek()>=4)
			{
				kmer=pop(kmer,false);
			}
			else if(stack.getLength() > this.distance)
			{
				if(seed2!=null)
				{
					kmer=pop(kmer,false);
				}
				else
				{
					kmer=pop(kmer,true);
				}	
			}
			else if(seed2!=null && kmer.length()>ks.getKmerSize() && kmer.indexOf(seed2.toString())!=-1)
			{
				kmer=pop(kmer,true);
			}
			else
			{
				//check last element
				//building up the kmer
				StringBuilder tempNewKmer = new StringBuilder(kmer.substring(kmer.length()-ks.getKmerSize(), kmer.length()));
				tempNewKmer.append(codingTable.getCodingTableEntry(stack.peek()));
				if(kmer.indexOf(tempNewKmer.toString()) !=-1)
				{
					//System.out.println("double kmer" + tempNewKmer);
					stack.setPeek(stack.peek()+1);
					setRepatPath(kmer);
				}
				else
				{					
					kmer.append(codingTable.getCodingTableEntry(stack.peek()));
					//if(counter % 100==0)
					//System.out.println("Path "+kmer.toString());
					//check Kmer
					//int a=kmer.length();
					//int b = ks.getKmerSize();
					//String test = kmer.substring(kmer.length()-ks.getKmerSize(), kmer.length());
					
					int kmerVal = checkKmerDistribution(kmer.substring(kmer.length()-ks.getKmerSize(), kmer.length()));
	//				if(kmerVal!=0)
	//				{
	//					System.out.println("Kmer "+kmer.substring(kmer.length()-ks.getKmerSize(), kmer.length())+" "+kmerVal);
	//					System.out.println("StackDeep: "+stack.getLength() + " " +stack.peek() +" Distance "+ distance +"\n");
	//				}
					if(kmerVal!=0 && kmerVal < repVal)
					{
						stack.push(0);
						++id;
					}
					else
					{
						if(kmerVal >=repVal)
						{
							//write log
							setRepatPath(kmer);
							//System.out.println("stopped by repKmer");
						}
						kmer.deleteCharAt(kmer.length()-1);
						stack.setPeek(stack.peek()+1);
					}
				}
			}
		}
		if(this.candidate.size()==0)
		{
			this.candidate.add(new StringBuilder(""));
		}
		setPaths();
	}
	
	
//	public List<StringBuilder> getCandidates()
//	{
//		return this.candidate;
//	}

	
	private void setRepatPath(StringBuilder kmer)
	{
		if(dir)
		{
			//System.out.println("kmer_r > rep");
			//System.out.println(sequenceUtils.reverseComplement2(kmer));
			if(this.repeatsStoppPath.size()==0)
			{
				this.repeatsStoppPath.add(sequenceUtils.reverseComplement2(kmer));
			}
			else if(!this.repeatsStoppPath.get(this.repeatsStoppPath.size()-1).equals(sequenceUtils.reverseComplement2(kmer)))
			{						
				this.repeatsStoppPath.add(sequenceUtils.reverseComplement2(kmer));
			}
			
		}
		else
		{
			
			//System.out.println("kmer_r > rep");
			if(this.repeatsStoppPath.size()==0)
			{
				this.repeatsStoppPath.add(kmer);
			}
			else if(this.repeatsStoppPath.size()>0&&!this.repeatsStoppPath.get(this.repeatsStoppPath.size()-1).equals(kmer))
			{
				this.repeatsStoppPath.add(kmer);
			}
		}
	}
	private void setPaths()
	{
		this.candidatePath.setFullpaths(this.candidate);
		this.candidatePath.setRepeatsEndedpaths(this.repeatsStoppPath);
	}
	
	public Path getPaths()
	{
		
		return this.candidatePath;
	}
	
	public Path call() throws Exception
	{
		return runIt();
	}
	
	public Path runIt()
	{
		run();
		return getPaths();
	}
	
	
//	public List<StringBuilder> call() throws Exception 
//	{
//			run();
//			
//		return getCandidates();
//	}
}
